import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import zipfile
import os
import threading
from datetime import datetime

class ZipExtractorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("ZIP文件解压工具")
        
        # 获取屏幕尺寸并设置合适的窗口大小
        screen_width = self.root.winfo_screenwidth()
        screen_height = self.root.winfo_screenheight()
        
        # 设置窗口大小为屏幕尺寸的70%
        window_width = int(screen_width * 0.7)
        window_height = int(screen_height * 0.7)
        
        # 设置窗口位置居中
        x = (screen_width - window_width) // 2
        y = (screen_height - window_height) // 2
        
        self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")
        self.root.minsize(700, 500)  # 设置最小尺寸
        
        # 设置应用图标
        try:
            self.root.iconbitmap("zip_icon.ico")  # 如果有图标文件的话
        except:
            pass
        
        # 设置样式
        self.style = ttk.Style()
        self.style.configure("TButton", padding=6, font=("微软雅黑", 10))
        self.style.configure("TLabel", font=("微软雅黑", 10))
        self.style.configure("Title.TLabel", font=("微软雅黑", 14, "bold"))
        
        # 创建自定义按钮样式（确保按钮可见）
        self.style.configure("Accent.TButton", 
                            background="#4CAF50", 
                            foreground="white", 
                            font=("微软雅黑", 10, "bold"))
        
        # 创建界面
        self.create_widgets()
        
        # 初始化变量
        self.zip_files = []
        self.dest_folder = ""
        self.extraction_thread = None
        self.stop_extraction = False

    def create_widgets(self):
        # 主框架，使用grid布局以获得更好的控制
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill="both", expand=True, padx=10, pady=10)
        
        # 配置grid权重，使各区域可以自适应
        main_frame.columnconfigure(0, weight=1)
        main_frame.rowconfigure(4, weight=1)  # 进度区域可以扩展
        
        # 标题
        title_label = ttk.Label(main_frame, text="ZIP文件批量解压工具", style="Title.TLabel")
        title_label.grid(row=0, column=0, pady=(0, 15), sticky="w")
        
        # 选择ZIP文件区域
        file_frame = ttk.LabelFrame(main_frame, text="选择ZIP文件")
        file_frame.grid(row=1, column=0, sticky="ew", pady=(0, 10))
        file_frame.columnconfigure(0, weight=1)
        file_frame.rowconfigure(0, weight=1)
        
        # 创建文件列表框架
        list_frame = ttk.Frame(file_frame)
        list_frame.grid(row=0, column=0, sticky="nsew", padx=10, pady=5)
        list_frame.columnconfigure(0, weight=1)
        list_frame.rowconfigure(0, weight=1)
        
        # 文件列表框和滚动条
        file_list_frame = ttk.Frame(list_frame)
        file_list_frame.grid(row=0, column=0, sticky="nsew")
        file_list_frame.columnconfigure(0, weight=1)
        file_list_frame.rowconfigure(0, weight=1)
        
        self.file_listbox = tk.Listbox(file_list_frame, selectmode=tk.EXTENDED)
        self.file_listbox.grid(row=0, column=0, sticky="nsew")
        
        file_scrollbar = ttk.Scrollbar(file_list_frame, orient="vertical", command=self.file_listbox.yview)
        file_scrollbar.grid(row=0, column=1, sticky="ns")
        self.file_listbox.config(yscrollcommand=file_scrollbar.set)
        
        # 按钮框架
        btn_frame = ttk.Frame(list_frame)
        btn_frame.grid(row=0, column=1, padx=(10, 0), sticky="ns")
        
        add_btn = ttk.Button(btn_frame, text="添加文件", command=self.add_files)
        add_btn.pack(fill="x", pady=5)
        
        remove_btn = ttk.Button(btn_frame, text="移除所选", command=self.remove_selected)
        remove_btn.pack(fill="x", pady=5)
        
        clear_btn = ttk.Button(btn_frame, text="清空列表", command=self.clear_list)
        clear_btn.pack(fill="x", pady=5)
        
        # 目标文件夹区域
        dest_frame = ttk.LabelFrame(main_frame, text="解压目标文件夹")
        dest_frame.grid(row=2, column=0, sticky="ew", pady=(0, 10))
        dest_frame.columnconfigure(0, weight=1)
        
        dest_inner_frame = ttk.Frame(dest_frame)
        dest_inner_frame.grid(row=0, column=0, sticky="ew", padx=10, pady=5)
        dest_inner_frame.columnconfigure(0, weight=1)
        
        self.dest_var = tk.StringVar()
        dest_entry = ttk.Entry(dest_inner_frame, textvariable=self.dest_var, state="readonly")
        dest_entry.grid(row=0, column=0, sticky="ew")
        
        browse_btn = ttk.Button(dest_inner_frame, text="浏览...", command=self.select_destination)
        browse_btn.grid(row=0, column=1, padx=(10, 0))
        
        # 选项区域
        options_frame = ttk.LabelFrame(main_frame, text="解压选项")
        options_frame.grid(row=3, column=0, sticky="ew", pady=(0, 10))
        
        # 创建子文件夹选项
        self.create_subfolders = tk.BooleanVar(value=True)
        subfolder_cb = ttk.Checkbutton(
            options_frame, 
            text="为每个ZIP文件创建单独的子文件夹",
            variable=self.create_subfolders
        )
        subfolder_cb.pack(anchor="w", padx=10, pady=5)
        
        # 覆盖文件选项
        self.overwrite_files = tk.BooleanVar(value=True)
        overwrite_cb = ttk.Checkbutton(
            options_frame, 
            text="覆盖已存在的文件",
            variable=self.overwrite_files
        )
        overwrite_cb.pack(anchor="w", padx=10, pady=5)
        
        # 进度区域 - 设置为可扩展
        progress_frame = ttk.LabelFrame(main_frame, text="进度")
        progress_frame.grid(row=4, column=0, sticky="nsew", pady=(0, 10))
        progress_frame.columnconfigure(0, weight=1)
        progress_frame.rowconfigure(2, weight=1)  # 日志区域可以扩展
        
        # 进度条
        self.progress_var = tk.IntVar()
        progress_bar = ttk.Progressbar(
            progress_frame, 
            variable=self.progress_var, 
            maximum=100
        )
        progress_bar.grid(row=0, column=0, sticky="ew", padx=10, pady=5)
        
        # 状态标签
        self.status_var = tk.StringVar(value="就绪")
        status_label = ttk.Label(progress_frame, textvariable=self.status_var)
        status_label.grid(row=1, column=0, sticky="w", padx=10, pady=(0, 5))
        
        # 日志区域 - 设置为可扩展
        log_frame = ttk.Frame(progress_frame)
        log_frame.grid(row=2, column=0, sticky="nsew", padx=10, pady=(0, 5))
        log_frame.columnconfigure(0, weight=1)
        log_frame.rowconfigure(0, weight=1)
        
        self.log_text = tk.Text(log_frame, height=8, state="disabled")
        self.log_text.grid(row=0, column=0, sticky="nsew")
        
        log_scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        log_scrollbar.grid(row=0, column=1, sticky="ns")
        self.log_text.config(yscrollcommand=log_scrollbar.set)
        
        # 操作按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.grid(row=5, column=0, sticky="ew", pady=(0, 5))
        button_frame.columnconfigure(0, weight=1)
        button_frame.columnconfigure(1, weight=1)
        
        # 左侧按钮组
        left_btn_frame = ttk.Frame(button_frame)
        left_btn_frame.grid(row=0, column=0, sticky="w")
        
        self.extract_btn = ttk.Button(
            left_btn_frame, 
            text="开始解压", 
            command=self.start_extraction,
            style="Accent.TButton"
        )
        self.extract_btn.pack(side=tk.LEFT, padx=5)
        
        self.stop_btn = ttk.Button(
            left_btn_frame, 
            text="停止解压", 
            command=self.stop_extraction_process,
            state="disabled"
        )
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        
        # 右侧按钮
        right_btn_frame = ttk.Frame(button_frame)
        right_btn_frame.grid(row=0, column=1, sticky="e")
        
        exit_btn = ttk.Button(right_btn_frame, text="退出", command=self.root.destroy)
        exit_btn.pack(side=tk.RIGHT, padx=5)
    
    def add_files(self):
        files = filedialog.askopenfilenames(
            title="选择ZIP文件",
            filetypes=[("ZIP压缩文件", "*.zip"), ("所有文件", "*.*")]
        )
        
        if files:
            for file in files:
                if file not in self.zip_files:
                    self.zip_files.append(file)
                    self.file_listbox.insert(tk.END, os.path.basename(file))
            
            self.log_message(f"添加了 {len(files)} 个ZIP文件")
    
    def remove_selected(self):
        selected = self.file_listbox.curselection()
        if not selected:
            return
            
        for index in selected[::-1]:
            self.zip_files.pop(index)
            self.file_listbox.delete(index)
        
        self.log_message(f"移除了 {len(selected)} 个文件")
    
    def clear_list(self):
        if not self.zip_files:
            return
            
        self.zip_files.clear()
        self.file_listbox.delete(0, tk.END)
        self.log_message("已清空文件列表")
    
    def select_destination(self):
        folder = filedialog.askdirectory(title="选择解压目标文件夹")
        if folder:
            self.dest_folder = folder
            self.dest_var.set(folder)
            self.log_message(f"目标文件夹设置为: {folder}")
    
    def log_message(self, message):
        timestamp = datetime.now().strftime("%H:%M:%S")
        full_message = f"[{timestamp}] {message}\n"
        
        self.log_text.config(state="normal")
        self.log_text.insert(tk.END, full_message)
        self.log_text.see(tk.END)
        self.log_text.config(state="disabled")
    
    def start_extraction(self):
        # 验证输入
        if not self.zip_files:
            messagebox.showwarning("警告", "请至少选择一个ZIP文件")
            return
            
        if not self.dest_folder:
            messagebox.showwarning("警告", "请选择解压目标文件夹")
            return
            
        # 创建目标文件夹（如果不存在）
        if not os.path.exists(self.dest_folder):
            try:
                os.makedirs(self.dest_folder)
                self.log_message(f"创建目标文件夹: {self.dest_folder}")
            except Exception as e:
                messagebox.showerror("错误", f"无法创建目标文件夹:\n{str(e)}")
                return
        
        # 禁用按钮并启用停止按钮
        self.extract_btn.config(state="disabled")
        self.stop_btn.config(state="normal")
        self.stop_extraction = False
        
        # 启动解压线程
        self.extraction_thread = threading.Thread(target=self.extract_files, daemon=True)
        self.extraction_thread.start()
    
    def stop_extraction_process(self):
        self.stop_extraction = True
        self.log_message("解压停止请求已发送...")
    
    def extract_files(self):
        total_files = len(self.zip_files)
        success_count = 0
        failure_count = 0
        
        self.log_message(f"开始解压 {total_files} 个ZIP文件...")
        self.status_var.set(f"处理中: 0/{total_files}")
        self.progress_var.set(0)
        
        for index, zip_file in enumerate(self.zip_files):
            if self.stop_extraction:
                break
                
            # 更新状态
            self.status_var.set(f"处理中: {index+1}/{total_files}")
            self.progress_var.set(int((index+1) / total_files * 100))
            self.log_message(f"正在解压: {os.path.basename(zip_file)}")
            
            try:
                # 确定解压路径
                if self.create_subfolders.get():
                    # 创建基于文件名的子文件夹
                    folder_name = os.path.splitext(os.path.basename(zip_file))[0]
                    extract_path = os.path.join(self.dest_folder, folder_name)
                    
                    # 确保目标文件夹存在
                    if not os.path.exists(extract_path):
                        os.makedirs(extract_path)
                else:
                    extract_path = self.dest_folder
                
                # 解压文件
                with zipfile.ZipFile(zip_file, 'r') as zip_ref:
                    # 获取文件列表用于进度显示
                    file_list = zip_ref.namelist()
                    total_members = len(file_list)
                    
                    for i, file in enumerate(file_list):
                        if self.stop_extraction:
                            break
                            
                        # 解压单个文件
                        try:
                            zip_ref.extract(file, extract_path)
                            
                            # 每解压10个文件或最后一个文件时更新日志
                            if i % 10 == 0 or i == total_members - 1:
                                self.log_message(f"  已解压: {file} ({i+1}/{total_members})")
                        except Exception as e:
                            self.log_message(f"  错误: 无法解压 {file} - {str(e)}")
                            failure_count += 1
                
                self.log_message(f"解压完成: {os.path.basename(zip_file)}")
                success_count += 1
            except Exception as e:
                self.log_message(f"解压失败: {os.path.basename(zip_file)} - {str(e)}")
                failure_count += 1
        
        # 更新状态
        self.status_var.set(f"完成: {success_count} 成功, {failure_count} 失败")
        self.log_message(f"解压完成! 成功: {success_count}, 失败: {failure_count}")
        
        # 启用按钮
        self.extract_btn.config(state="normal")
        self.stop_btn.config(state="disabled")
        
        # 显示摘要消息
        if not self.stop_extraction:
            message = f"解压完成!\n成功: {success_count} 个文件\n失败: {failure_count} 个文件"
            if failure_count == 0:
                messagebox.showinfo("完成", message)
            else:
                messagebox.showwarning("完成", message)
        else:
            messagebox.showinfo("已停止", "解压过程已停止")

# 运行应用
if __name__ == "__main__":
    root = tk.Tk()
    app = ZipExtractorApp(root)
    root.mainloop()
